package com.cgy.utils.base.cqengine;

import com.cgy.utils.base.classes.ClassHelper;
import com.cgy.utils.base.list.ListHelper;
import com.cgy.utils.base.strings.StringHelper;
import com.google.common.base.Splitter;
import com.googlecode.cqengine.ConcurrentIndexedCollection;
import com.googlecode.cqengine.IndexedCollection;
import com.googlecode.cqengine.attribute.Attribute;
import com.googlecode.cqengine.attribute.SimpleAttribute;
import com.googlecode.cqengine.attribute.support.SimpleFunction;
import com.googlecode.cqengine.index.Index;
import com.googlecode.cqengine.index.hash.HashIndex;
import com.googlecode.cqengine.index.navigable.NavigableIndex;
import com.googlecode.cqengine.index.radixreversed.ReversedRadixTreeIndex;
import com.googlecode.cqengine.index.suffix.SuffixTreeIndex;
import com.googlecode.cqengine.persistence.Persistence;
import com.googlecode.cqengine.query.Query;
import com.googlecode.cqengine.query.logical.And;
import com.googlecode.cqengine.query.logical.Or;
import com.googlecode.cqengine.query.option.OrderByOption;
import com.googlecode.cqengine.query.option.QueryOptions;
import com.googlecode.cqengine.query.simple.All;
import com.googlecode.cqengine.query.simple.None;
import com.googlecode.cqengine.resultset.ResultSet;
import org.apache.commons.lang3.math.NumberUtils;

import java.util.*;

import static com.cgy.utils.base.constant.Strings.EXTENSION_DOT;
import static com.cgy.utils.base.constant.Strings.EXTENSION_PER;
import static com.googlecode.cqengine.query.QueryFactory.*;
import static com.googlecode.cqengine.query.QueryFactory.or;

/**
 * @author CHTK
 */
public class CgEngine<T> {

    private static final String QUERY = "query";
    private static final String WHERE = "where";
    private static final String AND = "and";
    private static final String OR = "or";
    private static final String ORDERBY = "order by";
    private static final String LIKE = "like";
    private static final String GT = ">";
    private static final String LT = "<";
    private static final String BETWEEN = "between";

    private final T t = null;
    private Query<T> none;
    private Query<T> all;
    private static Map<String, Attribute> CACHE = new HashMap<>();

    public CgEngine(Class<T> clz) {
        none = new None<>(clz);
        all = new All<>(clz);
    }


    /**
     * 创建属性
     *
     * @return
     */
    public final <O, A> SimpleAttribute<O, A> createAttributeLambed(final String attributeName, SimpleFunction<O, A> function) {
        return attribute(attributeName, function);
    }

    /**
     * 创建属性
     *
     * @return
     */
    public final Attribute createAttribute(final String name, final SimpleFunction simpleFunction) {
        return attribute(name, simpleFunction);
    }

    /**
     * 创建属性
     *
     * @return
     */
    public final Attribute createAttribute(final String name) {
        return attribute(name, new SimpleFunction<Object, Object>() {
            @Override
            public Object apply(Object o) {
                return ClassHelper.invoke(o, name);
            }
        });
    }

    /**
     * 创建索引
     *
     * @return
     */
    public final <O, A> Index createSuffixTreeIndex(final String name, final SimpleFunction<O, A> simpleFunction) {
        final Attribute attribute = createAttribute(name, simpleFunction);
        return createSuffixTreeIndex(attribute);
    }

    /**
     * 创建索引
     *
     * @return
     */
    public final Index createSuffixTreeIndex(final Attribute attribute) {
        SuffixTreeIndex aoSuffixTreeIndex = SuffixTreeIndex.onAttribute(attribute);
        return aoSuffixTreeIndex;
    }

    /**
     * 创建索引
     *
     * @return
     */
    public final <O, A> Index createNavigableIndex(final String name, final SimpleFunction<O, A> simpleFunction) {
        final Attribute attribute = createAttribute(name, simpleFunction);
        return createNavigableIndex(attribute);
    }

    /**
     * 创建索引
     *
     * @return
     */
    public final Index createNavigableIndex(final Attribute attribute) {
        NavigableIndex navigableIndex = NavigableIndex.onAttribute(attribute);
        return navigableIndex;
    }

    /**
     * 创建索引
     *
     * @return
     */
    public final <O, A> Index createReversedRadixTreeIndex(final String name, final SimpleFunction<O, A> simpleFunction) {
        final Attribute attribute = createAttribute(name, simpleFunction);
        return createReversedRadixTreeIndex(attribute);
    }

    /**
     * 创建索引
     *
     * @return
     */
    public final Index createReversedRadixTreeIndex(final Attribute attribute) {
        ReversedRadixTreeIndex reversedRadixTreeIndex = ReversedRadixTreeIndex.onAttribute(attribute);
        return reversedRadixTreeIndex;
    }

    /**
     * 创建索引
     *
     * @return
     */
    public final <O, A> Index createHashIndex(final String name, final SimpleFunction<O, A> simpleFunction) {
        final Attribute attribute = createAttribute(name, simpleFunction);
        return createHashIndex(attribute);
    }

    /**
     * 创建索引
     *
     * @return
     */
    public final Index createHashIndex(final Attribute attribute) {
        HashIndex hashIndex = HashIndex.onAttribute(attribute);
        return hashIndex;
    }

    /**
     * @param list
     * @param query
     * @param indexes
     * @param <T>
     * @return
     */
    @Deprecated
    public final <T> List<T> result(final Query<T> query, final List<T> list, Index... indexes) {
        List<Index> indexList = new ArrayList<>();
        for (Index index : indexes) {
            indexList.add(index);
        }
        return result(query, list, indexList);
    }

    /**
     * @param list
     * @param indexes
     * @param query
     * @param <T>
     * @return
     */
    @Deprecated
    public final <T> List<T> result(final Query<T> query, final List<T> list, final List<Index> indexes) {
        List<T> result = null;

        if (null != list && !list.isEmpty()) {
            IndexedCollection<T> indexedCollection = new ConcurrentIndexedCollection<T>();
            for (Index index : indexes) {
                indexedCollection.addIndex(index);
            }
            indexedCollection.addAll(list);

            ResultSet<T> retrieve = indexedCollection.retrieve(query);

            result = new ArrayList<>(retrieve.size());

            for (T t : retrieve) {
                result.add(t);
            }
        }
        return result;
    }

    /**
     * @param srcList
     * @param fields
     * @return
     * @order(1)
     */
    public final <T> IndexedCollection<T> indexed(final List<T> srcList, final List<String> fields, final Persistence persistence) {

        IndexedCollection<T> indexedCollection = null;
        if(null == persistence) {
            indexedCollection = new ConcurrentIndexedCollection<T>();
        } else {
            indexedCollection = new ConcurrentIndexedCollection<T>(persistence);
        }

        Index index;
        Map<String, String> validFields = ClassHelper.paramNameAndType(ListHelper.only(srcList), fields);
        for (String validField : validFields.keySet()) {
            Attribute attribute = createAttribute(validField);
            CACHE.put(validField, attribute);
            if (String.class.getName().equals(validFields.get(validField))) {
                index = createSuffixTreeIndex(attribute);
            } else {
                index = createNavigableIndex(attribute);
            }
            indexedCollection.addIndex(index);
        }
        if (!ListHelper.isEmpty(srcList)) {
            indexedCollection.addAll(srcList);
        }
        return indexedCollection;
    }

    /**
     * 检索
     *
     * @param srcList 源数据
     * @param query   查询语句
     * @param fields  查询所有
     * @param <T>
     * @return
     */
    public final <T> List<T> result(final List<T> srcList, final Query<T> query, final List<String> fields, final OrderByOption orderByOption) {
        if (ListHelper.isEmpty(fields) || null == query) {
            return srcList;
        }

        IndexedCollection<T> indexedCollection = indexed(srcList, fields, null);
        return query(indexedCollection, query);
    }

    /**
     * 检索
     *
     * @param srcList 源数据
     * @param sql     查询语句
     * @param fields  查询所有
     * @param <T>
     * @return
     */
    public final <T> List<T> result(final List<T> srcList, final String sql, final List<String> fields) {
        IndexedCollection<T> indexedCollection = indexed(srcList, fields, null);
        Query<T> query = sql(ListHelper.one(srcList), sql);
        return query(indexedCollection, query);
    }

    /**
     * 检索
     *
     * @param srcList 源数据
     * @param sql     查询语句
     * @param fields  查询所有
     * @param <T>
     * @return
     */
    public final <T> List<T> result(final List<T> srcList, final String sql, final List<String> fields, final Persistence persistence) {
        IndexedCollection<T> indexedCollection = indexed(srcList, fields, persistence);
        Query<T> query = sql(ListHelper.one(srcList), sql);
        return query(indexedCollection, query);
    }

    /**
     * 检索
     *
     * @param srcList 源数据
     * @param sql     查询语句
     * @param <T>
     * @return
     */
    public final <T> List<T> result(final List<T> srcList, final String sql) {
        //name, id
        List<String> fields = StringHelper.cut(sql, QUERY, WHERE);

        List<String> fieldList = null;
        if (ListHelper.listSize(fields) > 0) {
            String one = ListHelper.one(fields);
            fieldList = Splitter.on(EXTENSION_DOT).trimResults().omitEmptyStrings().splitToList(one);
        }
        IndexedCollection<T> indexedCollection = indexed(srcList, fieldList, null);
        Query<T> query = sql(ListHelper.one(srcList), sql);
        return query(indexedCollection, query);
    }
    /**
     * 检索
     *
     * @param srcList 源数据
     * @param fields 索引字段
     * @param query     查询语句
     * @param <T>
     * @return
     */
    public final <T> List<T> result(final List<T> srcList, final List<String> fields, final Query<T> query) {
        List<String> fieldList = null;
        if (ListHelper.listSize(fields) > 0) {
            String one = ListHelper.one(fields);
            fieldList = Splitter.on(EXTENSION_DOT).trimResults().omitEmptyStrings().splitToList(one);
        }
        IndexedCollection<T> indexedCollection = indexed(srcList, fieldList, null);
        return query(indexedCollection, query);
    }

    /**
     * 查询
     *
     * @param indexedCollection
     * @param query
     * @param <T>
     * @return
     */
    public final <T> List<T> query(IndexedCollection<T> indexedCollection, Query<T> query) {
        QueryOptions queryOptions = new QueryOptions();

        ResultSet<T> retrieve = indexedCollection.retrieve(query);
        System.out.println(retrieve.toString());
        List<T> resultList = new ArrayList<>(retrieve.size());
        for (T t : retrieve) {
            resultList.add(t);
        }
        return resultList;
    }

    /**
     * @param t
     * @param sql <pre>
     *                query name, id where name like '%test' and id > 1 or name like 'test%' orde by name
     *            </pre>
     * @return
     */
    public final <T> Query<T> sql(T t, String sql) {
        //name, id
        List<String> field = StringHelper.cut(sql, QUERY, WHERE);
        //name like '%test' and id > 1 or name like 'test%'
        String conditions = StringHelper.startsWithString(sql, WHERE);
        //name like '%test' | id > 1 | name like 'test%
        List<String> splitter = StringHelper.splitters(conditions, AND, OR, ORDERBY);

        Attribute orderBy;

        List<Query<T>> lAnds = new ArrayList<>();
        List<Query<T>> lOrs = new ArrayList<>();
        for (String s : splitter) {
            String preffix = StringHelper.preffix(conditions, s, AND);
            if (StringHelper.isEmpty(preffix)) {
                continue;
            }
            Query<T> transform = transform(s);
            if (AND.equals(preffix)) {
                if (null != transform) {
                    lAnds.add(transform);
                }
            } else if (OR.equals(preffix)) {
                if (null != transform) {
                    lOrs.add(transform);
                }
            } else if (ORDERBY.equals(preffix)) {
                orderBy = CACHE.get(s);
            }
        }

        int andSize = lAnds.size();
        int orSize = lOrs.size();
        int sum = andSize + orSize;
        if(sum < 2) {
            System.out.println("当前参数为空不全查询条件, 不查询!!!!!!");
            return null;
        }

        And<T> and = and((Query<T>) all, (Query<T>) all, lAnds);
        Or<T> or = or((Query<T>) none, (Query<T>) none, lOrs);
        Query<T> query = or(and, or);
        return query;
    }

    /**
     * sql转化
     *
     * @param condition <pre>
     *                      e.q name like %test
     *                      e.q name like test%
     *                      e.q id > 1
     *                      e.q id between 1 and 2
     *                  </pre>
     * @param <T>
     * @return
     */
    private final <T> Query<T> transform(String condition) {
        if (condition.indexOf(LIKE) > -1) {
            List<String> strings = Splitter.on(LIKE).limit(2).splitToList(condition);
            final String field = StringHelper.trim(strings.get(0));
            final String con = StringHelper.trim(strings.get(1));
            if(con.endsWith(EXTENSION_PER) && con.startsWith(EXTENSION_PER)) {
                return containsWithSQL(field, StringHelper.remove(con, EXTENSION_PER));
            }
            if (con.endsWith(EXTENSION_PER)) {
                return startsWithSQL(field, StringHelper.remove(con, EXTENSION_PER));
            } else if (con.startsWith(EXTENSION_PER)) {
                return endsWithSQL(field, StringHelper.remove(con, EXTENSION_PER));
            }
        } else if (condition.indexOf(GT) > -1) {
            List<String> strings = Splitter.on(GT).limit(2).splitToList(condition);
            final String field = StringHelper.trim(strings.get(0));
            final String con = StringHelper.trim(strings.get(1));
            return greaterThanSQL(field, NumberUtils.toInt(con));
        } else if (condition.indexOf(LT) > -1) {
            List<String> strings = Splitter.on(LT).limit(2).splitToList(condition);
            final String field = StringHelper.trim(strings.get(0));
            final String con = StringHelper.trim(strings.get(1));
            return lessThanSQL(field, NumberUtils.toInt(con));
        } else if (condition.indexOf(BETWEEN) > -1) {
            List<String> strings = Splitter.on(LIKE).limit(2).splitToList(condition);
            final String field = StringHelper.trim(strings.get(0));
            final String con = StringHelper.trim(strings.get(1));
            if (con.indexOf(AND) > -1) {
                List<String> conditionBetween = Splitter.on(AND).limit(2).splitToList(condition);
                return betweenSQL(field, StringHelper.trim(conditionBetween.get(0)), StringHelper.trim(conditionBetween.get(1)));
            }

        }
        return null;
    }

    /**
     * @param value
     * @param field <pre>
     *              </pre>
     * @return
     */
    public final <T> Query<T> startsWithSQL(String field, String value) {
        Attribute attribute = CACHE.get(field.trim());
        return startsWith(attribute, value);
    }

    /**
     * @param value
     * @param field <pre>
     *              </pre>
     * @return
     */
    public final <T> Query<T> endsWithSQL(String field, String value) {
        Attribute attribute = CACHE.get(field.trim());
        return endsWith(attribute, value);
    }
    /**
     * @param value
     * @param field <pre>
     *              </pre>
     * @return
     */
    public final <T> Query<T> containsWithSQL(String field, String value) {
        Attribute attribute = CACHE.get(field.trim());
        return contains(attribute, value);
    }

    /**
     * @param value
     * @param field <pre>
     *              </pre>
     * @return
     */
    public final <T> Query<T> lessThanSQL(String field, int value) {
        Attribute attribute = CACHE.get(field.trim());
        return lessThan(attribute, value);
    }

    /**
     * @param value
     * @param field <pre>
     *              </pre>
     * @return
     */
    public final <T> Query<T> greaterThanSQL(String field, int value) {
        Attribute attribute = CACHE.get(field.trim());
        return greaterThan(attribute, value);
    }


    /**
     * @param start
     * @param end
     * @param field <pre>
     *              </pre>
     * @return
     */
    public final <T> Query<T> betweenSQL(String field, String start, String end) {
        Attribute attribute = CACHE.get(field.trim());
        return between(attribute, start, end);
    }


}
